﻿using UnityEngine;
using System.Collections.Generic;
using System.Text;
using UnityEditor;
using System.IO;
using UnityEditor.Animations;
using Unity.Mathematics;

namespace GameBasic.Editors
{
    public class BasicToolsWindow : EditorWindow
    {
        List<BaseTool> tools;
        const int DefaultBtnWidth = 120;

        [MenuItem(EditorGlobal.MenuItem_Tool + "Basic Tools")]
        public static void ShowWindow()
        {
            BasicToolsWindow win = (BasicToolsWindow)EditorWindow.GetWindow(typeof(BasicToolsWindow));
            win.Init();
        }

        void Init()
        {
            if (tools == null)
            {
                tools = new List<BaseTool>();
                tools.Add(new AssetsTools() { name = "Assets" });
                tools.Add(new ScreenshotTool() { name = "Screenshot" });
                tools.Add(new AnimatorCtrlTool() { name = "AnimatorController Property Tool" });
                tools.Add(new AnimationImportTool() { name = "Import Settings: Animation" });
            }
        }

        void OnGUI()
        {
            Init();

            for (int i = 0, length = tools.Count; i < length; i++)
                tools[i].OnGUI();
        }

        public interface ITool
        {
            void OnGUI();
        }

        public abstract class BaseTool : ITool
        {
            public string name;
            public bool enabled;

            // head
            protected GUIStyle headStyle;

            public BaseTool()
            {
                //headStyle = new GUIStyle(GUI.skin.label);
                headStyle = new GUIStyle();
                headStyle.fontSize = 16;
            }

            public virtual void OnGUI()
            {
                EditorGUILayout.BeginVertical();
                DrawHead();
                OnGUIImpl();
                EditorGUILayout.EndVertical();
            }

            protected abstract void OnGUIImpl();

            protected void DrawHead()
            {
                EditorGUILayout.LabelField("");
                EditorGUILayout.LabelField(name, headStyle, GUILayout.Height(24));
            }

            protected void DrawTextField(string label, ref string text, float labelWidth = 50, float fieldWidth = 180)
            {
                EditorGUILayout.LabelField(label, GUILayout.Width(labelWidth));
                text = EditorGUILayout.TextField(text, GUILayout.Width(fieldWidth));
            }
        }

        public class AssetsTools : BaseTool
        {
            int updateCount;
            string typeText = "";
            string savedText = "Reulsts";
            string names = "";
            StringBuilder strBuilder = new StringBuilder();

            protected override void OnGUIImpl()
            {
                //
                EditorGUILayout.BeginHorizontal();
                EditorGUILayout.LabelField("Type", GUILayout.Width(50));
                typeText = EditorGUILayout.TextField(typeText, GUILayout.Width(150));

                if (GUILayout.Button("Refresh", GUILayout.Width(60)))
                {
                    bool isType = typeText.Length > 2 && typeText[0] == 't' && typeText[1] == ':';

                    if (isType)
                    {
                        string[] guids2 = AssetDatabase.FindAssets(typeText);
                        foreach (string guid2 in guids2)
                        {
                            string path = AssetDatabase.GUIDToAssetPath(guid2);
                            // Debug.Log(path);
                            Object obj = AssetDatabase.LoadAssetAtPath(path, typeof(Object));
                            if (obj != null)
                            {
                                // display 5
                                if (updateCount < 5)
                                {
                                    strBuilder.Append(obj.name).Append("\n");
                                }else if(updateCount == 5)
                                {
                                    strBuilder.Append("...");
                                }

                                EditorUtility.SetDirty(obj);
                                updateCount++;
                            }
                        }

                        AssetDatabase.SaveAssets();
                        AssetDatabase.Refresh();
                    }
                }

                EditorGUILayout.EndHorizontal();

                // Results
                if(updateCount > 0)
                {
                    savedText = "Saved: " + updateCount;
                    names = strBuilder.ToString();
                    strBuilder.Clear();
                    updateCount = 0;
                }

                EditorGUILayout.LabelField(savedText);
                EditorGUILayout.TextArea(names);
            }
        }

        public class ScreenshotTool : BaseTool
        {
            string path;

            protected override void OnGUIImpl()
            {
                EditorGUILayout.BeginHorizontal();
                DrawTextField("Path", ref path);
                if (GUILayout.Button("Screenshot", GUILayout.Width(DefaultBtnWidth)))
                {
                    if (!string.IsNullOrEmpty(path))
                    {
                        ScreenCapture.CaptureScreenshot(path);

                        // Capature();
                    }
                }
                EditorGUILayout.EndHorizontal();
            }

            void Capature()
            {
                Camera cam = Camera.main;

                int width = cam.pixelWidth;
                int height = cam.pixelHeight;
                Debug.Log("Render: " + width + ", " + height);
                RenderTexture renderTex = new RenderTexture(width, height, 32);

                // Capature cam
                cam.targetTexture = renderTex;
                cam.Render();

                // Texture2D
                RenderTexture.active = renderTex;
                // false, meaning no need for mipmaps
                Texture2D tex = new Texture2D(width, height, TextureFormat.ARGB32, false);
                // you get the center section
                tex.ReadPixels(new Rect(0, 0, width, height), 0, 0);

                // Save
                File.WriteAllBytes(path, tex.EncodeToPNG());

                // Clear
                RenderTexture.active = null;
                cam.targetTexture = null;
                //tricky on android and other platforms, take care
                DestroyImmediate(renderTex);
            }
        }

        public class AnimatorCtrlTool : BaseTool
        {
            struct Data
            {
                public string name;
                public int nameHash;
                public string text;
            }

            System.Comparison<Data> compareName = (a, b) => string.Compare(a.name, b.name);

            string paraText;
            string stateText;

            protected override void OnGUIImpl()
            {
                //EditorGUILayout.LabelField("");
                //EditorGUILayout.LabelField(name, headStyle, GUILayout.Height(24));

                // Buttons
                EditorGUILayout.BeginHorizontal();
                bool clear = GUILayout.Button("Clear", GUILayout.Width(DefaultBtnWidth));

                //
                if (GUILayout.Button("Anim Hash", GUILayout.Width(DefaultBtnWidth)))
                {
                    // Selected Asset
                    if (GetAnimatorController(out AnimatorController animCtrl))
                    {
                        // Parameters
                        List<Data> paraDatas = GetParas(animCtrl);

                        // StateDatas
                        List<Data> stateDatas = GetAnims(animCtrl);

                        // Sort Data by name
                        //paraDatas.Sort(compareName);
                        stateDatas.Sort(compareName);

                        // Text
                        StringBuilder sb = new StringBuilder(stateDatas.Count * 32);

                        Append(sb, paraDatas);
                        paraText = sb.ToString();

                        sb.Clear();
                        Append(sb, stateDatas);
                        stateText = sb.ToString();
                    }
                    // Clear
                    else
                    {
                        clear = true;
                    }
                }

                if (GUILayout.Button("Motion Info", GUILayout.Width(DefaultBtnWidth)))
                {
                    // Selected Asset
                    if (GetAnimatorController(out AnimatorController animCtrl))
                    {
                        // StateDatas
                        List<Data> stateDatas = GetAnims(animCtrl);

                        // Sort Data by name
                        //paraDatas.Sort(compareName);
                        stateDatas.Sort(compareName);

                        // Text
                        StringBuilder sb = new StringBuilder(stateDatas.Count * 32);
                        AppendDataText(sb, stateDatas);
                        stateText = sb.ToString();
                    }
                    // Clear
                    else
                    {
                        clear = true;
                    }
                }

                // Code Gen
                if (GUILayout.Button("Code Gen", GUILayout.Width(DefaultBtnWidth)))
                {
                    if (GetAnimatorController(out AnimatorController animCtrl))
                    {
                        // Parameters
                        List<Data> paraDatas = GetParas(animCtrl);

                        // StateDatas
                        List<Data> stateDatas = GetAnims(animCtrl);
                        stateDatas.Sort(compareName);

                        // Text
                        paraText = CodeGenAnim(paraDatas, true);
                        stateText = CodeGenAnim(stateDatas, false);
                        EditorGUIUtility.systemCopyBuffer = paraText + "\n\n" + stateText;
                    }
                    // Clear
                    else
                    {
                        clear = true;
                    }
                }

                if (clear)
                {
                    paraText = "";
                    stateText = "";
                }
                EditorGUILayout.EndHorizontal();

                // TextArea
                EditorGUILayout.BeginHorizontal();
                GUILayout.TextArea(paraText, GUILayout.Width(250));
                GUILayout.TextArea(stateText, GUILayout.Width(250));
                EditorGUILayout.EndHorizontal();
            }

            bool GetAnimatorController(out AnimatorController animCtrl)
            {
                return GetAnimatorController(Selection.activeObject, out animCtrl);
            }

            static bool GetAnimatorController(Object obj, out AnimatorController animCtrl)
            {
                animCtrl = obj as AnimatorController;

                // HasComponent?
                if (animCtrl == null)
                {
                    GameObject gameObj = obj as GameObject;

                    if (gameObj != null)
                    {
                        Animator animator = gameObj.GetComponent<Animator>();
                        if (animator != null)
                            animCtrl = animator.runtimeAnimatorController as AnimatorController;
                    }
                }

                return animCtrl != null;
            }

            List<Data> GetParas(AnimatorController animCtrl)
            {
                List<Data> paraDatas = new List<Data>();
                var paras = animCtrl.parameters;
                for (int i = 0; i < paras.Length; i++)
                {
                    var para = paras[i];
                    paraDatas.Add(new Data()
                    {
                        name = para.name,
                        nameHash = para.nameHash
                    });
                }

                return paraDatas;
            }

            List<Data> GetAnims(AnimatorController animCtrl)
            {
                List<Data> stateDatas = new List<Data>();

                animCtrl.ForeachState((int3 loop, AnimatorState state) => 
                {
                    Data data = new Data()
                    {
                        name = state.name,
                        nameHash = state.nameHash,
                    };

                    AnimationClip clip = state.motion as AnimationClip;
                    if (clip != null)
                        data.text = clip.length.ToString();
                    stateDatas.Add(data);
                });

                return stateDatas;
            }

            void Append(StringBuilder sb, List<Data> datas)
            {
                for (int i = 0, length = datas.Count; i < length; i++)
                {
                    var state = datas[i];
                    sb.Append(state.nameHash).Append("\t").Append(state.name).Append("\n");
                }
            }

            void AppendDataText(StringBuilder sb, List<Data> datas)
            {
                for (int i = 0, length = datas.Count; i < length; i++)
                {
                    var state = datas[i];
                    sb.Append(state.nameHash).Append("\t").Append(state.name).Append(": ").Append(state.text).Append("\n");
                }
            }

            string CodeGenAnim(List<Data> datas, bool para)
            {
                StringBuilder sb0 = new StringBuilder(datas.Count * 32);
                StringBuilder sb1 = new StringBuilder(datas.Count * 32);
                StringBuilder sb2 = new StringBuilder(datas.Count * 16);

                string prefix = para ? "P_" : "";
                // Region Start
                sb0.Append("#region ").Append(para ? "Animator Properties" : "Animator States").Append("\n");

                sb2.Append("public static readonly int[] ").Append( para ? "ParaHashs = {" : "AnimHashs = {" );
                for (int i = 0, length = datas.Count; i < length; i++)
                {
                    var state = datas[i];
                    // index
                    sb0.Append("public const int ").Append(prefix).Append(state.name).Append("_I = ").Append(i).Append(";\n");
                    // hash
                    sb1.Append("public const int ").Append(prefix).Append(state.name).Append(" = ").Append(state.nameHash).Append(";\n");
                    //
                    sb2.Append(prefix).Append(state.name).Append(", ");
                }
                sb2.Append("};");

                sb0.Append("\n").Append(sb1).Append("\n").Append(sb2);
                // Region
                sb0.Append("\n#endregion");

                return sb0.ToString();
            }
        }

        public class AnimationImportTool : BaseTool
        {
            string typeText;
            protected override void OnGUIImpl()
            {
                EditorGUILayout.BeginHorizontal();
                //
                EditorGUILayout.LabelField("Root Motion Node", GUILayout.Width(150));
                typeText = EditorGUILayout.TextField(typeText, GUILayout.Width(150));
                if (GUILayout.Button("Set", GUILayout.Width(DefaultBtnWidth)))
                {
                    Object[] objs = Selection.objects;
                    if (objs == null)
                        return;

                    foreach (Object obj in objs)
                    {
                        string assetPath = AssetDatabase.GetAssetPath(obj);
                        ModelImporter modelImporter = AssetImporter.GetAtPath(assetPath) as ModelImporter;
                        if (modelImporter == null)
                            return;

                        modelImporter.motionNodeName = typeText;

                        SerializedObject modelImporterObj = new SerializedObject(modelImporter);

                        modelImporterObj.ApplyModifiedProperties();

                        AssetDatabase.ImportAsset(assetPath);
                    }
                }
                EditorGUILayout.EndHorizontal();
            }
        }
    }
}